package learn_concurrent._6;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// 同步模式 保护性暂停
public class T1 {
	static final Logger logger = LoggerFactory.getLogger(T1.class);

	public static void main(String[] args) throws InterruptedException {
		Person person1 = new Person("person1");
		Person person2 = new Person("person2");
		person1.start();
		person2.start();
		
		Thread.sleep(1000);
		new Postman("postman1", person1.getBoxId(), "To person1 : hello there!").start();
//		Thread.sleep(1000);
		new Postman("postman2", person2.getBoxId(), "To person2 : hi...").start();
	}

}

class Person extends Thread {
	private static final Logger logger = LoggerFactory.getLogger(Person.class);
	private int boxId;
	
	public Person(String name) {
		super(name);
	}

	@Override
	public void run() {
		GuardedObject box = MailBoxes.createMailBox();
		this.boxId = box.getId();
		Object mail = box.get(5000);
		logger.info("recive id {}, mail : {}", box.getId(), mail);
	}
	
	public int getBoxId() {
		return boxId;
	}
}

class Postman extends Thread {
	private static final Logger logger = LoggerFactory.getLogger(Postman.class);
	
	private final int boxId;
	private final String mail;
	
	public Postman(String name, int boxId, String mail) {
		super(name);
		this.boxId = boxId;
		this.mail = mail;
	}

	@Override
	public void run() {
		GuardedObject box = MailBoxes.getBox(boxId);
		logger.info("post a mail...");
		box.complete(mail);
	}
}

class MailBoxes {
	private static Map<Integer, GuardedObject> boxes = new Hashtable<>();
	private static int id = 1;
	
	public static GuardedObject createMailBox() {
		GuardedObject guardedObject = new GuardedObject(generateId());
		boxes.put(guardedObject.getId(), guardedObject);
		return guardedObject;
	}

	public static Set<Integer> getIds() {
		return boxes.keySet();
	}
	
	public static GuardedObject getBox(int id) {
		return boxes.remove(id);
	}
	
	private static synchronized int generateId() {
		return id++;
	}
	
}

class GuardedObject {
	private int id;
	private Object response;

	public GuardedObject(int id) {
		this.id = id;
	}
	
	public int getId() {
		return id;
	}

	public Object get(long timeout) {
		synchronized (this) {
			long startTime = System.currentTimeMillis();
			long passedTime = 0;

			while (response == null) {
				long waitTime = timeout - passedTime;
				if (waitTime <= 0) {
					break;
				}
				try {
					this.wait(waitTime);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				passedTime = System.currentTimeMillis() - startTime;
			}
			return response;
		}
	}

	public void complete(Object value) {
		synchronized (this) {
			response = value;
			this.notifyAll();
		}
	}
}
